package com.xxl.job.admin.core.model;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.dimples.dd.common.enums.CommonStatusEnum;
import com.dimples.dd.common.spring.SpringContextHolder;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xxl.job.core.biz.ExecutorBiz;
import com.xxl.job.core.biz.client.ExecutorBizClient;
import com.xxl.job.core.biz.model.ReturnT;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.stream.Collectors;

/**
 * Created by xuxueli on 16/9/30.
 */
@Slf4j
@Data
public class XxlJobGroup {

    private int id;

    private String appname;

    private String title;
    /**
     * 执行器地址类型：0=自动注册、1=手动录入
     */
    private int addressType;
    /**
     * 执行器地址列表，多地址逗号分隔(手动录入)
     */
    private String addressList;

    @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8")
    private Date updateTime;
    /**
     * 执行器所有地址整体状态 （1：全部正常，2：全部异常，3：部分异常）
     */
    private Integer status;

    // registry list
    private List<RegistryBean> registryList;  // 执行器地址列表(系统注册)

    public List<RegistryBean> getRegistryList() {
        registryList = CollUtil.newArrayList();
        if (addressList != null && !addressList.trim().isEmpty()) {
            // address 执行器管理填入的机器地址 http://ip:port 这里自定义兼容 lb://serviceName地址
            List<String> tmpAddressList = CollUtil.newArrayList();
            for (String addr : StrUtil.split(addressList, StrUtil.COMMA)) {
                if (StrUtil.startWith(addr, "lb:")) {
                    String serviceName = StrUtil.replace(addr, "lb://", "");
                    DiscoveryClient discoveryClient = SpringContextHolder.getBean(DiscoveryClient.class);
                    List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
                    List<String> uriList = instances.stream().map(a -> a.getUri().toString()).toList();
                    tmpAddressList.addAll(uriList);
                } else {
                    tmpAddressList.add(addr);
                }
            }

            for (String addr : StrUtil.split(StrUtil.join(StrUtil.COMMA, tmpAddressList), StrUtil.COMMA)) {
                RegistryBean registryBean = new RegistryBean();
                registryBean.setAppname(this.getAppname());
                registryBean.setAddress(addr);
                registryBean.setStatus(CommonStatusEnum.DISABLE.getValue());
                registryList.add(registryBean);
            }

            Map<String, Integer> statusMap = MapUtil.newHashMap();
            this.getAddressStatus(statusMap, registryList);

            for (XxlJobGroup.RegistryBean registryBean : registryList) {
                Integer status = statusMap.get(registryBean.getAddress());
                if (ObjectUtil.isNotEmpty(status)) {
                    registryBean.setStatus(status);
                }
            }
        }
        return registryList;
    }

    public Integer getStatus() {
        List<RegistryBean> registryBeanList = this.getRegistryList();
        if (CollUtil.isNotEmpty(registryBeanList)) {
            Set<Integer> statusSet = registryBeanList.stream().map(RegistryBean::getStatus).collect(Collectors.toSet());
            if (CollUtil.size(statusSet) > 1) {
                return 3;
            } else {
                if (ObjectUtil.equals(CollUtil.getFirst(statusSet), CommonStatusEnum.ENABLE.getValue())) {
                    return 1;
                } else {
                    return 2;
                }
            }
        } else {
            return 2;
        }
    }

    @Data
    public static class RegistryBean {

        private String appname;

        private String address;

        private Integer status;
    }

    private void getAddressStatus(Map<String, Integer> statusMap, List<XxlJobGroup.RegistryBean> registryList) {
        CompletionService<Map<String, Integer>> completionService = ThreadUtil.newCompletionService();
        List<Callable<Map<String, Integer>>> task = CollUtil.newArrayList();

        for (XxlJobGroup.RegistryBean address : registryList) {
            task.add(() -> {
                ExecutorBiz executorBiz = new ExecutorBizClient(address.getAddress(), null);
                ReturnT<String> beat = executorBiz.beat();
                Map<String, Integer> tmpMap = MapUtil.newHashMap();
                if (beat.getCode() == ReturnT.SUCCESS_CODE) {
                    tmpMap.put(address.getAddress(), CommonStatusEnum.ENABLE.getValue());
                } else {
                    statusMap.put(address.getAddress(), CommonStatusEnum.DISABLE.getValue());
                }
                return tmpMap;
            });
        }

        for (Callable<Map<String, Integer>> returnTCallable : task) {
            completionService.submit(returnTCallable);
        }

        for (int i = 0; i < task.size(); i++) {
            try {
                Map<String, Integer> returnT = completionService.take().get();
                statusMap.putAll(returnT);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }
}
